/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sirtet.netplay.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import sirtet.netplay.ClientResponse;
import sirtet.netplay.Communicator;
import sirtet.netplay.NetworkPlayer;
import sirtet.netplay.ServerResponse;
import sirtet.netplay.event.CommunicationDataEvent;
import sirtet.netplay.event.CommunicationEvent;
import sirtet.netplay.event.CommunicationListener;

/**
 * This class handles the incoming client calls and raise event whenever
 * a new call is accepted.
 * @author guangwei.zhu
 */
public final class Receptionist extends ServerObject {

    private ServerSocket ssock;
    private boolean stopSignal;
    private Thread receptionthread = new ListeningThread();

    public Receptionist(int port) throws IOException {
        // Create server socket
        ssock = new ServerSocket(port);
    }

    /**
     * Begin waiting for incoming calls.
     */
    public synchronized void beginWait() {
        while (stopSignal) {
            Thread.yield();
        }

        if (!receptionthread.isAlive()) {
            stopSignal = false;
            receptionthread.start();
        }
    }

    /**
     * Stop waiting for incoming calls.
     */
    public synchronized void stopWait() {
        if (receptionthread.isAlive()) {
            stopSignal = true;
            receptionthread = new ListeningThread();
        }
    }

    private class ListeningThread extends Thread {

        @Override
        public void run() {
            Socket csock;
            Communicator client;

            while (true) {
                try {
                    if (stopSignal) {
                        break;
                    }
                    ssock.setSoTimeout(1000);   // Release every 1 second
                    csock = ssock.accept();
                    client = new Communicator(csock);
                    client.activate();

                    new Representative(client); // Handle the socket and create player
                } catch (SocketTimeoutException ex) {
                    continue;   // for awakening
                } catch (IOException ex) {
                    break;
                }
            }

            stopSignal = false;
        }
    }

    private class Representative implements CommunicationListener {

        private Communicator communicator;
        private String playerID;
        private String gameType;

        public Representative(Communicator comm) {
            this.communicator = comm;
            comm.addCommunicationListener(this);

            this.askPlayerID();
        }

        public void askPlayerID() {
            try {
                this.communicator.send(ServerResponse.ASK_PLAYERID);
            } catch (Exception e) {
                this.dispose();
            }
        }

        public void askGameType() {
            try {
                this.communicator.send(ServerResponse.ASK_GAMETYPE);
            } catch (Exception e) {
                this.dispose();
            }
        }

        /**
         * Calling this method will result in disposal of the representative
         */
        protected void dispose() {
            this.communicator.removeCommunicationListener(this);
        }

        public void communicationDisconnected(CommunicationEvent evt) {
        }

        public void communicationError(CommunicationEvent evt) {
        }

        public void communicationDataReceived(CommunicationDataEvent evt) {
            try {
                String id = ClientResponse.parse(ClientResponse.PREFIX_PLAYERID, evt.data);
                if (id != null) {   // The response is not about telling ID
                    playerID = id;
                }

                String type = ClientResponse.parse(ClientResponse.PREFIX_GAMETYPE, evt.data);
                if (type != null) {   // The response is not about telling ID
                    gameType = type;
                }

                if (playerID == null || playerID.isEmpty()) {
                    playerID = null;
                    askPlayerID();
                    return;
                }

                if (gameType == null || gameType.isEmpty()) {
                    gameType = null;
                    askGameType();
                    return;
                }

                NetworkPlayer player = new NetworkPlayer(playerID, gameType, true, communicator);

                // Accept by default, change if you want to check for dup id
                communicator.send(ServerResponse.ACCEPTED);

                notifyPlayerArrived(player);
            } catch (Exception ex) {
                communicator.disconnect();
            }
            this.dispose();     // Detach representative to cause GC
        }
    }
}
